package main

import (
	"crypto/md5"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	_ "sync"
	"time"
	_ "web/memory"
	"web/session"
)

// 全局session管理器
var globalSession *session.Manager

// init函数中初始化
func init() {
	globalSession, _ = session.NewManager("memory", "gosessionid", 3600)
	go globalSession.GC()
}

/*
r.Form中包含了所有的请求数据，比如URL中的query-string,POST数据、PUT数据，所有当你在URL的
query-string字段和POST冲突时，会保存成为一个slice，里面存储了多个值
*/

// 根路由
func sayhelloName(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() // 解析url传递的参数，对于POST则解析响应包的主体(request body)
	fmt.Println(r.Form)
	fmt.Println("path:", r.URL.Path)
	fmt.Println("schema:", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for k, v := range r.Form {
		fmt.Println("key:", k)
		fmt.Println("value:", v)
	}
	fmt.Fprint(w, "Hello LewisJoe") // 这个写入到w的是输出到客户端的
}

// session 测试
func login2(w http.ResponseWriter, r *http.Request) {
	sess := globalSession.SessionStart(w, r)
	r.ParseForm()
	if r.Method == "GET" {
		t, _ := template.ParseFiles("login.html")
		w.Header().Set("Content-Type", "text/html") // 头设置
		t.Execute(w, sess.Get("username"))
	} else {
		sess.Set("username", r.Form["username"])
		http.Redirect(w, r, "/", 302) // 跳转
	}
}

// count计数器
func count(w http.ResponseWriter, r *http.Request) {
	sess := globalSession.SessionStart(w, r)
	// 间隔生成新的sid，防止session劫持
	createtime := sess.Get("createtime")
	if createtime == nil {
		sess.Set("createtime", time.Now().Unix())
	} else if (createtime.(int64) + 360) < (time.Now().Unix()) {
		globalSession.SessionDestory(w, r)
		sess = globalSession.SessionStart(w, r)
	}
	ct := sess.Get("countnum")
	if ct == nil {
		sess.Set("countnum", 1)
	} else {
		sess.Set("countnum", (ct.(int) + 1))
	}
	t, _ := template.ParseFiles("count.html")
	w.Header().Set("Content-Type", "text/html")
	t.Execute(w, sess.Get("countnum"))
}

// 文件上传路由
func upload(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method)
	if r.Method == "GET" {
		// 读取cookie
		cookie, err := r.Cookie("username")
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		fmt.Println("In upload the cookie is:", cookie)
		curTime := time.Now().Unix()
		h := md5.New()
		io.WriteString(h, strconv.FormatInt(curTime, 10))
		token := fmt.Sprintf("%x", h.Sum(nil))
		fmt.Printf("%v", r.Header)
		fmt.Println()
		t, _ := template.ParseFiles("upload.html")
		t.Execute(w, token)
	} else {
		r.ParseMultipartForm(32 << 20)
		file, handler, err := r.FormFile("uploadfile")
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		defer file.Close()
		fmt.Fprintf(w, "%v", handler.Header)
		executefile, _ := exec.LookPath(os.Args[0])
		//path, _ := filepath.Abs(executefile)
		path := filepath.Dir(executefile)
		f, err := os.OpenFile(path+"\\test\\"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		defer f.Close()
		io.Copy(f, file)
	}
}

// 登录路由
func login(w http.ResponseWriter, r *http.Request) {
	// 默认情况下，Handler中是不会自动解析form的，必须显式调用r.ParseForm()后，你才能对这个
	// 表单数据进行操作。
	r.ParseForm()
	fmt.Println("method:", r.Method) // 获取请求的方法
	if r.Method == "GET" {
		// 读取cookie
		for _, v := range r.Cookies() {
			fmt.Println(v)
		}
		curTime := time.Now().Unix()
		h := md5.New()
		io.WriteString(h, strconv.FormatInt(curTime, 10))
		token := fmt.Sprintf("%x", h.Sum(nil))
		t, err := template.ParseFiles("login.html")
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		t.Execute(w, token)
	} else {
		token := r.Form.Get("token")
		if token != "" {
			// 验证token的合法性
			// template.HTMLEscape(w, []byte(r.Form.Get("token")))
			fmt.Println("token:", template.HTMLEscapeString(r.Form.Get("token")))
		} else {
			// 不存在token报错
		}
		name := template.HTMLEscapeString(r.Form.Get("username"))
		fmt.Println("username:", name)
		// 设置Cookie
		// Set-Cookie由服务器发送，它包含在响应请求的头部中。它用于在客户端创建一个Cookie
		// Cookie头由客户端发送，包含在HTTP请求的头部中。注意，只有cookie的domain和path与请求的URL匹配才会发送这个cookie。
		// expires:设置cookie的有效期，如果cookie超过date所表示的日期时，cookie将失效。如果没有设置这个选项，那么cookie将在浏览器关闭时失效
		// 临时cookie(没有expires参数的cookie)不能带有domain选项。
		// 如果一个cookie的domain和path参数和URL匹配，那么这个cookie就是有效的。
		exporation := time.Now()
		exporation = exporation.AddDate(1, 0, 0)
		cookie := &http.Cookie{
			Name:    "username",
			Value:   name,
			Expires: exporation,
		}
		fmt.Println("Cookie in post:", cookie.String())
		http.SetCookie(w, cookie)
	}
}

// 自定义实现的路由
type MyMux struct {
}

func (m *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/":
		sayhelloName(w, r)
		return
	case "/login":
		login(w, r)
		return
	case "/upload":
		upload(w, r)
		return
	case "/login2":
		login2(w, r)
		return
	case "/count":
		count(w, r)
		return
	}
	http.NotFound(w, r)
	return
}

func main() {
	/*
		http.HandleFunc("/", sayhelloName)       // 设置访问的路由
		err := http.ListenAndServe(":9090", nil) // 设置监听的端口
		if err != nil {
			log.Fatal("ListenAndServer:", err.Error())
		}*/
	mux := &MyMux{}
	err := http.ListenAndServe(":9090", mux)
	if err != nil {
		log.Fatal("ListenAndServer error", err.Error())
	}
}
